// This loads the data from the cldr web site
//
// Specifically it has to load the URL for the cldr web site and parse it for version #s
// then it tells the mainform about the various versions.
// Once a version is set by the main form we have to download its data,
// then decompress and parse that.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using ionic.utils.zip;

namespace customcldr
{
    // This class get list of versions from web site
    class Download
    {
        Uri ourURI = null;
        MainForm mainForm = null;

        public Download(String URL, MainForm mainForm)
        {
            this.ourURI = new Uri(URL);

            this.mainForm = mainForm;
        }

        // Get list of versions from web site
        string href = "href=\"";
        string endAtag = "</a>";
        internal void GetVersions()
        {
            string strPage = GetPage(ourURI);

            Console.WriteLine(strPage);

            String[] strings = strPage.Split(new char[] { '\r', '\n' });

            ArrayList versions = new ArrayList();

            for (int i = 0; i < strings.Length; i++)
            {
                int j;
                if ((j = strings[i].IndexOf(href)) == -1) continue;

                String temp = strings[i].Substring(j+href.Length);

                if (temp[0] < '0' || temp[0] > '1') continue;

                j = temp.IndexOf('"');

                Uri verUri = new Uri(ourURI.ToString() + temp.Substring(0, j));
                string strLabel = temp.Substring(0, j - 1) + "\t";

                j = temp.IndexOf(endAtag);
                temp = temp.Substring(j + endAtag.Length);

                j = temp.IndexOf(" -");
                if (j > 0) temp = temp.Substring(0, j);

                temp.Trim();
                strLabel += temp;

                CldrVersionInfo version = new CldrVersionInfo(verUri, strLabel);
                versions.Add(version);
            }

            this.mainForm.SetVersions(versions);
        }

        internal void SetVersion(CldrVersionInfo ver)
        {
            Uri verUri = ver.Version;
            verUri = new Uri(verUri.ToString() + "core.zip");

            String fileName = verUri.ToString();
            fileName = fileName.Replace(':', '_');
            fileName = fileName.Replace('/', '_');
            fileName = fileName.Replace('\\', '_');
           
            Console.WriteLine("pick version " + verUri );
            Console.WriteLine("dl file " + fileName);

            mainForm.ChangeLoading("Loading...");
            string zipDir = null;
            if (!File.Exists(fileName) || (zipDir = Unzip(fileName)) == null)
            {
                GetZipFile(verUri, fileName);
                zipDir = Unzip(fileName);
// when you do not want to unzip all the time just uncomment this and comment the above one.
//zipDir = "C:\\projects\\customcldr\\customcldr\\http___unicode.org_Public_cldr_1.4.0_core\\main\\";
            }

            if (zipDir != null)
            {
                GetLocales(zipDir);
            }

            mainForm.ChangeLoading("");
        }

        private void GetLocales(string strDir)
        {
            string[] strFiles = Directory.GetFiles(strDir);
            this.mainForm.SetCldrFiles(strFiles);
        }

        private string Unzip(String fileName)
        {
            String TargetDirectory = fileName.Replace(".zip", "\\");

            try 
            {
                ZipFile zip = ZipFile.Read(fileName, false); 
                zip.ExtractDir(TargetDirectory, "main", false);
            }
            catch (System.Exception ex1)
            {
                System.Console.Error.WriteLine("exception: " + ex1);
                return null;
            }
            return TargetDirectory + "main\\";
        }

        private string GetPage(Uri pageUri)
        {
            // This is copied from an example at:
            // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnetsocketsprotocoltypeclasstopic.asp
            
            //Set up variables and String to write to the pageUri.Host.
            Encoding ASCII = Encoding.ASCII;
            string Get = "GET " + pageUri + " HTTP/1.1\r\nHost: " + pageUri.Host +
                         "\r\nConnection: Close\r\n\r\n";
            Byte[] ByteGet = ASCII.GetBytes(Get);
            Byte[] RecvBytes = new Byte[256];
            String strRetPage = null;

            // IPAddress and IPEndPoint represent the endpoint that will
            //   receive the request.
            // Get first IPAddress in list return by DNS.
            try
            {
                // Define those variables to be evaluated in the next for loop and 
                // then used to connect to the pageUri.Host. These variables are defined
                // outside the for loop to make them accessible there after.
                Socket s = null;
                IPEndPoint hostEndPoint;
                IPAddress hostAddress = null;
                int conPort = 80;

                // Get DNS host information.
                IPHostEntry hostInfo = Dns.GetHostEntry(pageUri.Host);
                // Get the DNS IP addresses associated with the host.
                IPAddress[] IPaddresses = hostInfo.AddressList;

                // Evaluate the socket and receiving host IPAddress and IPEndPoint. 
                for (int index=0; index<IPaddresses.Length; index++)
                {
                    hostAddress = IPaddresses[index];
                    hostEndPoint = new IPEndPoint(hostAddress, conPort);

                    // Creates the Socket to send data over a TCP connection.
                    s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );

                    // Connect to the host using its IPEndPoint.
                    s.Connect(hostEndPoint);

                    if (!s.Connected)
                    {
                        // Connection failed, try next IPaddress.
                        strRetPage = "Unable to connect to host";
                        s = null;
                        continue;
                    }

                    // Sent the GET request to the host.
                    s.Send(ByteGet, ByteGet.Length, 0);
                } // End of the for loop.      

                // Receive the host home page content and loop until all the data is received.
                Int32 bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);
                strRetPage = "Default HTML page on " + pageUri.Host + ":\r\n";
                strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);

                while (bytes > 0)
                {
                    bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);
                    strRetPage = strRetPage + ASCII.GetString(RecvBytes, 0, bytes);
                }
            } // End of the try block.
            
            catch(SocketException e) 
            {
              Console.WriteLine("SocketException caught!!!");
              Console.WriteLine("Source : " + e.Source);
              Console.WriteLine("Message : " + e.Message);
            }
            catch(ArgumentNullException e)
            {
              Console.WriteLine("ArgumentNullException caught!!!");
              Console.WriteLine("Source : " + e.Source);
              Console.WriteLine("Message : " + e.Message);
            }
            catch(NullReferenceException e)
            {
              Console.WriteLine("NullReferenceException caught!!!");
              Console.WriteLine("Source : " + e.Source);
              Console.WriteLine("Message : " + e.Message);
            }
            catch(Exception e)
            {
              Console.WriteLine("Exception caught!!!");
              Console.WriteLine("Source : " + e.Source);
              Console.WriteLine("Message : " + e.Message);
            }

            return strRetPage;
        }

        private string GetZipFile(Uri fileUri, String useFileName)
        {
            // This copied from a sample at:
            // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemnetsocketsprotocoltypeclasstopic.asp

            //Set up variables and String to write to the fileUri.Host.
            Encoding ASCII = Encoding.ASCII;
            string Get = "GET " + fileUri + " HTTP/1.1\r\nHost: " + fileUri.Host +
                         "\r\nConnection: Close\r\n\r\n";
            Byte[] ByteGet = ASCII.GetBytes(Get);
            Byte[] RecvBytes = new Byte[256];

            // IPAddress and IPEndPoint represent the endpoint that will
            //   receive the request.
            // Get first IPAddress in list return by DNS.
            try
            {
                // Define those variables to be evaluated in the next for loop and 
                // then used to connect to the fileUri.Host. These variables are defined
                // outside the for loop to make them accessible there after.
                Socket s = null;
                IPEndPoint hostEndPoint;
                IPAddress hostAddress = null;
                int conPort = 80;

                // Get DNS host information.
                IPHostEntry hostInfo = Dns.GetHostEntry(fileUri.Host);
                // Get the DNS IP addresses associated with the host.
                IPAddress[] IPaddresses = hostInfo.AddressList;

                // Evaluate the socket and receiving host IPAddress and IPEndPoint. 
                for (int index = 0; index < IPaddresses.Length; index++)
                {
                    hostAddress = IPaddresses[index];
                    hostEndPoint = new IPEndPoint(hostAddress, conPort);

                    // Creates the Socket to send data over a TCP connection.
                    s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

                    // Connect to the host using its IPEndPoint.
                    s.Connect(hostEndPoint);

                    if (!s.Connected)
                    {
                        // Connection failed, try next IPaddress.
                        useFileName = "Unable to connect to host";
                        s = null;
                        continue;
                    }

                    // Sent the GET request to the host.
                    s.Send(ByteGet, ByteGet.Length, 0);
                } // End of the for loop.      

                // Receive the host home page content and loop until all the data is received.
                Int32 bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);
                
                // get past header bytes
                int CRLF = 0;
                int iStart = 0;
     
                while (bytes > 0)
                {
                    for (int i = 0; i < RecvBytes.Length; i++)
                    {
                        if (RecvBytes[i] == (byte)'\r')
                        {
                            if (CRLF == 0 || CRLF == 2) CRLF++;
                            else CRLF = 0;
                        }
                        else if (RecvBytes[i] == (byte)'\n')
                        {
                            if (CRLF == 1) CRLF++;
                            else if (CRLF == 3)
                            { iStart = i + 1; CRLF++; break; } // found \r\n\r\n so header done
                            else CRLF = 0;
                        }
                        else CRLF = 0;
                    }

                    if (CRLF == 4) break; // found \r\n\r\n so header done

                    bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);                
                }

                if (iStart == bytes)
                    bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);                

//                useFileName = Path.GetTempFileName();
                Console.WriteLine("using temp file " + useFileName);
                FileStream file = File.OpenWrite(useFileName);

                while (bytes > 0)
                {
                    file.Write(RecvBytes, iStart, bytes - iStart);
                    iStart = 0;
                    bytes = s.Receive(RecvBytes, RecvBytes.Length, 0);
                }
                file.Close();
            } // End of the try block.

            catch (SocketException e)
            {
                Console.WriteLine("SocketException caught!!!");
                Console.WriteLine("Source : " + e.Source);
                Console.WriteLine("Message : " + e.Message);
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("ArgumentNullException caught!!!");
                Console.WriteLine("Source : " + e.Source);
                Console.WriteLine("Message : " + e.Message);
            }
            catch (NullReferenceException e)
            {
                Console.WriteLine("NullReferenceException caught!!!");
                Console.WriteLine("Source : " + e.Source);
                Console.WriteLine("Message : " + e.Message);
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception caught!!!");
                Console.WriteLine("Source : " + e.Source);
                Console.WriteLine("Message : " + e.Message);
            }

            return useFileName;
        }
    }
}
